闲话 22.11.19

闲话

感谢 apj 先生让我登上了博客园

最近闲话阅读量怎么忽高忽低的?
一天高一天低了可以说是
随便摘了一篇高的 11.9
怎么是字符串专题啊?
这么喜欢看题解为什么不去洛谷给我题解点赞(
但其实似乎有一部分是因为评论数吧(

杂题

[P5087] 数学 加强版

小奔热衷于乘法,他最喜欢做的事情是:从一个有 \(n\) 个元素的可重集中选出 \(k\) 个数,并把这 \(k\) 个数的乘积作为这个组合的分数。

小奔想试遍所有的这些组合,然后算出所有这些组合的分数之和。但是他还要出模拟赛虐爆我们这些蒟蒻,所以他只好把这个任务交给了你。

作为不良心的出题人,这题你还要将答案对 \(10^9 + 7\) 取模。

\(1\le k \le n \le 1.2\times 10^5\)\(1\le a_i \le 10^8\)

感谢 H_Kaguya 和 crs_line 推荐的这道水题

一眼 dp 式子 \(f_{i,j} = a_i\times f_{i-1,j-1} + f_{i-1,j}\),考虑分配律可得。
然后对两边按 \(j\) 求和可得 \(F_{i} = a_i x F_{i-1} + F_{i-1}\),即 \(F_i = (a_i x + 1) F_{i-1}\)
答案即为

\[[x^k]\prod_{i=1}^n (a_i x + 1) \]

分治滚一遍就行了。出题人不良心所以贺了原来的 mtt 过来。拿了最优解?但不管怎么说 1e6 开氧气跑 1.85s 还是有点小快的。

总时间复杂度 \(O(n\log^2 n)\)

code
#include <bits/stdc++.h>
using namespace std;
template <typename T> void get(T & x) {
	x = 0; char ch = getchar(); bool f = false; while (ch < '0' or ch > '9') f = f or ch == '-', ch = getchar();
	while ('0' <= ch and ch <= '9') x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); f && (x = -x); 
} template <typename T, typename ... Args> void get(T & a, Args & ... b) { get(a); get(b...); }
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
const int N = 1e6 + 10, mod = 1e9 + 7;

poly F[N];
int n, k, t;
poly calc(int l, int r) {
    if (l == r) return F[l];
    int mid = l + r >> 1;
    return conv(calc(l, mid), calc(mid + 1, r), mod);
}

signed main() {
    get(n, k);
    rep(i,1,n) get(t), F[i].resize(2), F[i][0] = 1, F[i][1] = t;
    cout << calc(1, n)[k];
}



AGC053D

有一场 \(n\) 个参赛者、\(n\) 道题的比赛。参赛者被编号为 \(1\)\(n\)。我们都知道每一名参赛者解决每一道题目需要的时间,其只可能是 \(1/2/3\) 分钟。在这 \(n\) 道题目中,有 \(A_i\) 道题目需要参赛者 \(i\) 花费 \(1\) 分钟解决,有 \(B_i\) 道题目需要参赛者 \(i\) 花费 \(2\) 分钟解决,有 \(C_i\) 道题目需要参赛者 \(i\) 花费 \(3\) 分钟解决。

假设每名参赛者都能够自由决定做题顺序,请确定如下的条件是否能够对于所有参赛者 \(1\le i,j\le n\) 都成立:

  • \(S\) 为参赛者 \(i\) 完成前 \(i\) 道题目的时间,\(T\) 为参赛者 \(j\) 完成前 \(i\) 道题目的时间。条件即为 \(S\le T\)

具体的说,在忽略切换题目的时间的情况下,需要确定是否对于每个参赛者 \(i\),其为所有人中第一个(可能为并列)完成前 \(i\) 道题目的人。

\(T\) 组询问。

\(1\le T\le 2\times 10^5,\ 1\le n \le 2\times 10^5, \ 0\le A_i,B_i,C_i\le n,\ A_i + B_i + C_i = n\)。保证所有询问的 \(n\) 加和 \(\le 2\times 10^5\)

\(T_i\) 最开始为所有人都优先做更耗时的题目的情况下,第一个做出前 \(i\) 道题的人花费的时间。参赛者 \(i\) 做出前 \(i\) 道题的时间应当不大于 \(T_i\) 分钟。

我们动态更新每个 \(T_i\)。倒序考虑每名参赛者,假设现在已经考虑到了第 \(i\) 名。
如果第 \(i\) 名参赛者无法在 \(T_i\) 分钟内做出前 \(i\) 道题,则该组询问的答案为否。反之可以选出耗时最长且加和不超过 \(T_i\)\(i\) 道题,从更耗时的题目开始做。剩余的 \(n-i\) 道题在做完前 \(i\) 道题后开始做,同样从更耗时的题目开始。假设其做完前 \(j\) 道题消耗了 \(s_{i,j}\) 分钟,则对于每个 \(1\le j < i\),更新 \(T_j = \min(T_j, s_{i,j})\)
如果成功决策则该组询问的答案为是。

但上面的决策方式可能会让人有疑问:对于 \(i<j\le n\),我们是否一定能保证 \(s_{i,j} \ge T_j\) 呢?换句话说,这种方式是否会打乱已经完成的决策呢?我们断言,在花费的时间仅有 \(\{1,2,3\}\) 分钟的情况下,一定能保证 \(s_{i,j} \ge T_j\)

对于 \(2\le i\le n\),假设我们在对 \(i\) 决策后有 \(T_{i-1} + 2 \ge T_i\),则对于任意的 \(j \ge i\),我们都能有 \(T_j \le T_i + 2(j - i)\)。可以发现该条件在该情况下是必定满足的,因此该贪心策略是正确的。但其并不适用于推广后的情况。

因此我们可以记录 \(T_i\) 的后缀最小值,随后对于每个点 \(O(1)\) 地判定是否满足。

总时间复杂度 \(O(n)\)

code
#include <bits/stdc++.h>
using namespace std; 
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
const int N = 2e5 + 10, inf = 0x3f3f3f3f;
int n, a[N], b[N], c[N];

void solve() {
	cin >> n; rep(i,1,n) cin >> a[i] >> b[i] >> c[i];
	int f0 = inf, f1 = inf;
	rep(i,1,n) f0 = min(f0, c[i] * 2 + b[i]), f1 = min(f1, c[i]);
	bool ck = true;
	pre(i,n,1) {
		int mn = min( { f0, f1 + i, i << 1 } );
		int l = max(0, i - a[i] - b[i]), r = min( { c[i], i, mn >> 1 } );
		if (l > r or i - mn + l > a[i]) { ck = false; break; }
		r = min(r, a[i] - i + mn);
		f0 = min( { f0, mn, r + i, (r << 1) + b[i] } );
		f1 = min( { f1, r } );
	} puts(ck ? "Yes" : "No");
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); 
	int T; cin >> T; while (T--) solve(); return 0; 
}
posted @ 2022-11-19 20:04  joke3579  阅读(47)  评论(2编辑  收藏  举报